from typing import List

from fastapi import Depends
from sqlalchemy.ext.asyncio import AsyncSession

from app.api.remote.auth import IAuthClient
from app.constants import REDIS_KEY_USER_PERMISSION_KEY, REDIS_KEY_USER_ROLE_KEY, LOGIN_TYPE, REDIS_KEY_DEFAULT_EXPIRE
from app.core.database import get_session
from app.core.redis import redis_client
from app.exception import NotPermissionException, NotRoleException
from app.utils import context


async def get_db() -> AsyncSession:
    """
    获取数据库会话（依赖注入用）

    通过异步上下文管理器获取数据库会话，确保会话正确关闭。

    :yield: 数据库会话对象
    """
    async with get_session() as session:
        yield session


async def get_permission_list() -> List[str]:
    """
    获取当前账号所拥有的权限码集合（依赖注入用）

    从Redis缓存中获取用户权限列表，如果缓存不存在则通过远程调用获取并缓存。

    :return: 用户权限码列表
    """
    # 从上下文中获取用户id
    user_id = context.get_user_id()

    async def get_user_permission_list() -> List[str]:
        # 远程调用获取权限码集合
        return await IAuthClient.permissions(loginId=user_id, loginType=LOGIN_TYPE)

    # 从redis中获取权限码集合, 如果为空则远程调用获取, 返回结果并将结果缓存到redis中
    return await redis_client.get_or_set(
        key=f"{REDIS_KEY_USER_PERMISSION_KEY}:{user_id}",
        callable_func=get_user_permission_list,
        expire=REDIS_KEY_DEFAULT_EXPIRE
    )


async def get_role_list() -> List[str]:
    """
    获取当前账号所拥有的角色标识集合（依赖注入用）

    从Redis缓存中获取用户角色列表，如果缓存不存在则通过远程调用获取并缓存。

    :return: 用户角色标识列表
    """
    user_id = context.get_user_id()

    async def get_user_role_list() -> List[str]:
        # 远程调用获取角色标识集合
        return await IAuthClient.roles(loginId=user_id, loginType=LOGIN_TYPE)

    # 从redis中获取角色标识集合,如果为空则远程调用获取, 返回结果并将结果缓存到redis中
    return await redis_client.get_or_set(
        key=f"{REDIS_KEY_USER_ROLE_KEY}:{user_id}",
        callable_func=get_user_role_list,
        expire=REDIS_KEY_DEFAULT_EXPIRE
    )


def require_permission(permission_code: str):
    """
    权限验证（依赖注入用）

    用于验证当前用户是否具有指定权限。

    :param permission_code: 需要验证的权限码
    :return: 权限验证依赖项
    """

    def _dep(permission_list: List[str] = Depends(get_permission_list)) -> bool:
        """
        :param permission_list: 用户权限列表 依赖注入
        :return: 验证结果
        :raises NotPermissionException: 当用户不具有指定权限时抛出
        """
        # 判断用户拥有的权限列表中是否包含指定权限码
        if permission_code not in permission_list:
            raise NotPermissionException(permission=permission_code)
        return True

    return _dep


def require_role(role_code: str):
    """
    角色验证（依赖注入用）

    用于验证当前用户是否具有指定角色。

    :param role_code: 需要验证的角色标识
    :return: 权限验证依赖项
    """

    def _dep(role_list: List[str] = Depends(get_role_list)) -> bool:
        """
        :param role_list: 用户角色列表 依赖注入
        :return: 验证结果
        :raises NotRoleException: 当用户不具有指定权限时抛出
        """
        # 判断用户拥有的角色列表中是否包含指定角色标识
        if role_code not in role_list:
            raise NotRoleException(role=role_code)
        return True

    return _dep
